package com.ems.remote;

import com.ems.bo.CheckFrameBo;
import com.ems.bo.FrameBo;
import com.ems.config.StrategyFactory;
import com.ems.remote.components.PointStrategy;
import com.ems.service.IBatteryBoxService;
import com.ems.service.IPcsService;
import com.ems.util.HexUtil;
import com.ems.util.StrategyUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: Wuzh
 * @Description:
 * @Date Created in 23:16 2023-12-28
 * @return
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class ServerChannelRequest extends SimpleChannelInboundHandler<byte[]> {

    @Resource
    private StrategyFactory strategyFactory;
    @Resource
    private IPcsService iPcsService;
    @Resource
    private IBatteryBoxService iBatteryBoxService;

    private static List<CheckFrameBo> list = new ArrayList<> ();
    private static String partFrame; // 部分帧内容


    @Override
    protected void channelRead0 ( ChannelHandlerContext channelHandlerContext, byte[] bytes ) {
        Channel channel = channelHandlerContext.channel ();
        if ( channel.isActive () ) {
            String s = HexUtil.bytesToHexString ( bytes );
            log.info ( "初始报文->{}", s );
            //处理下有效报文
            FrameBo frameBo = new FrameBo ();
            frameBo.setDeviceAddressHigh ( bytes[4] );
            frameBo.setDeviceAddressLow ( bytes[5] );
            parseData ( s, frameBo, channelHandlerContext );
        }
    }

    /**
     * 解析请求帧数据，根据请求帧的命令码解析不同数据类型
     *
     * @param data：请求帧数据：0c 00 0c 05 1a 0c 01 66 73 6a 79 23 25 40 32 30 31 36 15 0d
     *                      c  0  d  7  1a  19  2 00 00 00 01 2f 84 00 01 00 01 04 28  fa  d
     * @param ctx
     */
    private void parseData ( String data, FrameBo frameBo, ChannelHandlerContext ctx ) {
        frameBo.setCommandCode ( HexUtil.parseData ( data, 3, 1 ) );
        //根据命令码解决对应的业务帧
        if ( StrategyUtil.EMS_CONTROL_FRAME_REQUEST.equals ( frameBo.getCommandCode () ) ||
                StrategyUtil.EMS_LOGIN_FRAME_REQUEST.equals ( frameBo.getCommandCode () ) ||
                StrategyUtil.EMS_HEAT_FRAME_REQUEST.equals ( frameBo.getCommandCode () ) ||
                StrategyUtil.EMS_CONNECT_QUERY_REQUEST.equals ( frameBo.getCommandCode () ) ) {
            int length = HexUtil.hexToDecimal ( data, 1, 2 );
            frameBo.setDataLength ( length );
            //设备地址
            frameBo.setDeviceAddress ( HexUtil.parseData ( data, 4, 2 ) );
            //数据内容，根据数据长度截取
            String s = HexUtil.parseData ( data, 6, data.length () / 2 - 2 - 6 );
            frameBo.setData ( s );
            log.info ( "命令码->{}, 设备地址->{}, 数据->{}", frameBo.getCommandCode (), frameBo.getDeviceAddress (), s );
            //校验码
            frameBo.setCheckCode ( HexUtil.parseData ( data, 6 + length, 1 ) );
            IStrategy iStrategy = strategyFactory.getStrategy ( StrategyUtil.getKey ( frameBo.getCommandCode () ) );
            iStrategy.action ( frameBo, ctx );
        }
        if ( "07".equals ( frameBo.getCommandCode () ) || "08".equals ( frameBo.getCommandCode () ) ) {
            //07\08\没有头的报文
            list = parseFrameData ( data );
            for ( int i = list.size () - 1; i >= 0; i-- ) {
                String commandCode = HexUtil.parseData ( list.get ( i ).getFrameData (), 3, 1 );
                if ( checkFrame ( list.get ( i ).getFrameData () ) && ! StrategyUtil.EMS_HEAT_FRAME_REQUEST.equals ( commandCode ) ) {
                    int length = HexUtil.hexToDecimal ( list.get ( i ).getFrameData (), 1, 2 );
                    frameBo.setDataLength ( length );
                    frameBo.setCommandCode ( commandCode );
                    //3.设备地址
                    frameBo.setDeviceAddress ( HexUtil.parseData ( list.get ( i ).getFrameData (), 4, 2 ) );
                    //4.数据内容，根据数据长度截取
                    String con = HexUtil.parseData ( list.get ( i ).getFrameData (), 6, length + 8 );
                    //5.校验码
                    frameBo.setCheckCode ( HexUtil.parseData ( list.get ( i ).getFrameData (), 6 + length, 1 ) );
                    frameBo.setData ( con );
                    PointStrategy.action ( frameBo, iPcsService, iBatteryBoxService );
                }
            }
            for ( int i = list.size () - 1; i >= 0; i-- ) {
                if ( list.get ( i ).getFlag () ) {
                    list.remove ( i );
                }
            }
        }
    }


    /**
     * 拆包、组包
     *
     * @param data
     * @return
     */
    public static List<CheckFrameBo> parseFrameData ( String data ) {
        data = data.replace ( " ", "" ).trim ();
        int index = 0;
        int strLen = data.length () / 2; // 剩下整个字符串的长度
        int frameLen = 0; // 每一帧实际数据长度
        do {
            String start = HexUtil.parseData ( data, index, 1 );//0c
            //先处理没头，既然没有头，就拿到所有的，后面也许是一帧，也许是多帧；拿到后先拿前面list报文中不完整报文去拼接
            if ( ! "0c".equals ( start ) ) {
                // 没有的多帧报文，接上面没有尾的报文，然后继续组报文
                String s = partFrame + data;
                //把上面字符串清空
                partFrame = "";
                parseFrameData ( s );
                break;
            }
            //处理每一帧数据
            frameLen = HexUtil.hexToDecimal ( data, index + 1, 2 ) + 8;//正常每一帧实际数据长度
            String frameData = HexUtil.parseData ( data, index, frameLen );//剩余的数据
            if ( frameLen > strLen ) {
                // 不够一帧，没有尾的报文，接后面没有头的报文
                partFrame = frameData;
                break;
            }
            if ( frameLen == strLen ) {
                //当前就一帧或最后一帧
                CheckFrameBo checkFrameBo = new CheckFrameBo ();
                checkFrameBo.setFrameData ( frameData );
                checkFrameBo.setFlag ( true );
                list.add ( checkFrameBo );
                break;
            }
            if ( frameLen < strLen ) {
                //剩余字符串够解析至少一帧数据
                index += frameLen;
                CheckFrameBo checkFrameBo = new CheckFrameBo ();
                checkFrameBo.setFrameData ( frameData );
                checkFrameBo.setFlag ( true );
                list.add ( checkFrameBo );
            }
            strLen = strLen - frameLen; // 整个字符串的长度
        }
        while ( true );
        return list;
    }


    /**
     * 检查一帧报文是否符合格式要求
     *
     * @param frame
     * @return
     */
    public static boolean checkFrame ( String frame ) {
        String start = HexUtil.parseData ( frame, 0, 1 );//0c
        int length = HexUtil.hexToDecimal ( frame, 1, 2 ) + 8;
        if ( "0c".equals ( start ) && length == frame.length () / 2 ) {
            return true;
        }
        return false;
    }

    /**
     * 活跃的、有效的通道
     * 第一次连接成功后进入的方法
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive ( ChannelHandlerContext ctx ) throws Exception {
        super.channelActive ( ctx );
        log.info ( "tcp client " + getRemoteAddress ( ctx ) + " connect success" );
    }


    /**
     * 不活动的通道
     * 连接丢失后执行的方法（client端可据此实现断线重连）
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive ( ChannelHandlerContext ctx ) {
        // 删除Channel Map中失效的Client
        log.info ( "tcp client " + getRemoteAddress ( ctx ) + " 删除Channel" );

        ctx.close ();
    }


    /**
     * 异常处理
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught ( ChannelHandlerContext ctx, Throwable cause ) throws Exception {
        super.exceptionCaught ( ctx, cause );
        // 发生异常 关闭连接
        log.error ( "引擎{}的通道发生异常，断开连接", getRemoteAddress ( ctx ) );
        ctx.close ();
    }

    /**
     * 心跳机制 超时处理
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered ( ChannelHandlerContext ctx, Object evt ) throws Exception {
        String socketString = ctx.channel ().remoteAddress ().toString ();
        if ( evt instanceof IdleStateEvent ) {
            IdleStateEvent event = ( IdleStateEvent ) evt;
            if ( event.state () == IdleState.READER_IDLE ) {
                log.info ( "Client: " + socketString + " READER_IDLE读超时" );
                ctx.disconnect ();
            } else if ( event.state () == IdleState.WRITER_IDLE ) {
                log.info ( "Client: " + socketString + " WRITER_IDLE写超时" );
                ctx.disconnect ();
            } else if ( event.state () == IdleState.ALL_IDLE ) {
                log.info ( "Client: " + socketString + " ALL_IDLE总超时" );
                ctx.disconnect ();
            }
        }
    }

    /**
     * 获取client对象：ip+port
     *
     * @param channelHandlerContext
     * @return
     */
    private String getRemoteAddress ( ChannelHandlerContext channelHandlerContext ) {
        String socketString = "";
        socketString = channelHandlerContext.channel ().remoteAddress ().toString ();
        return socketString;
    }

    /**
     * 获取client的ip
     *
     * @param channelHandlerContext
     * @return
     */
    private String getIPString ( ChannelHandlerContext channelHandlerContext ) {
        String ipString = "";
        String socketString = channelHandlerContext.channel ().remoteAddress ().toString ();
        int colonAt = socketString.indexOf ( ":" );
        ipString = socketString.substring ( 1, colonAt );
        return ipString;
    }


}
